added io-bmp from Arjan <arjan@fenrus.demon.nl>
authorJonathan Blandford <jrb@redhat.com>
Sun, 21 Nov 1999 21:28:28 +0000 (21:28 +0000)
committerJonathan Blandford <jrb@src.gnome.org>
Sun, 21 Nov 1999 21:28:28 +0000 (21:28 +0000)
1999-11-21  Jonathan Blandford  <jrb@redhat.com>

        * src/gdk-pixbuf-io.c: added io-bmp from Arjan
        <arjan@fenrus.demon.nl>

gdk-pixbuf/ChangeLog
gdk-pixbuf/Makefile.am
gdk-pixbuf/gdk-pixbuf-io.c
gdk-pixbuf/io-bmp.c

index 786890b7c8a20e621e75115898028226e8d220c8..2bbf215361affb4182d9f902ad27f309079bd8e1 100644 (file)
@@ -1,3 +1,8 @@
+1999-11-21  Jonathan Blandford  <jrb@redhat.com>
+
+       * src/gdk-pixbuf-io.c: added io-bmp from Arjan
+       <arjan@fenrus.demon.nl>
+
 1999-11-20  Michael Zucchi  <zucchi@zedzone.mmc.com.au>
 
        * src/gdk-pixbuf-drawable.c (gdk_pixbuf_from_drawable_core):
index 9d453dfd04492f7f2d60c946015582dfbc9aa7cc..c6bc0e7b9b70b968d3c89d40f441160b7d1dc6ab 100644 (file)
@@ -23,6 +23,8 @@ XPM_LIB =     libpixbuf-xpm.la
 
 PNM_LIB =      libpixbuf-pnm.la
 
+BMP_LIB =      libpixbuf-bmp.la
+
 libexec_LTLIBRARIES =          \
        $(PNG_LIB)      \
        $(JPEG_LIB)     \
@@ -30,7 +32,8 @@ libexec_LTLIBRARIES =         \
        $(RAS_LIB)      \
        $(XPM_LIB)      \
        $(TIFF_LIB)     \
-       $(PNM_LIB)
+       $(PNM_LIB)      \
+       $(BMP_LIB)
 
 noinst_PROGRAMS = testpixbuf testpixbuf-drawable
 
@@ -117,3 +120,10 @@ libpixbuf_tiff_la_LIBADD = $(LIBTIFF)
 libpixbuf_pnm_la_SOURCES = io-pnm.c
 libpixbuf_pnm_la_LDFLAGS = -avoid-version -module
 libpixbuf_pnm_la_LIBADD = 
+
+#
+# The BMP loader
+#
+libpixbuf_bmp_la_SOURCES = io-bmp.c
+libpixbuf_bmp_la_LDFLAGS = -avoid-version -module
+libpixbuf_bmp_la_LIBADD =
index b139d4e1fcc7277ccf66d2305be7672af2c53964..f884f484fb6c5f625a73abb901fd1aae42cd88b9 100644 (file)
@@ -136,7 +136,6 @@ pixbuf_check_sunras (guchar *buffer, int size)
 }
 
 
-#if 0
 static gboolean
 pixbuf_check_bmp (guchar *buffer, int size)
 {
@@ -149,7 +148,6 @@ pixbuf_check_bmp (guchar *buffer, int size)
        return TRUE;
 }
 
-#endif
 
 GdkPixbufModule file_formats [] = {
        { "png",  pixbuf_check_png, NULL,  NULL, NULL, NULL, NULL, NULL },
@@ -160,9 +158,7 @@ GdkPixbufModule file_formats [] = {
        { "xpm",  pixbuf_check_xpm, NULL,  NULL, NULL, NULL, NULL, NULL },
        { "pnm",  pixbuf_check_pnm, NULL,  NULL, NULL, NULL, NULL, NULL },
        { "ras",  pixbuf_check_sunras, NULL,  NULL, NULL, NULL, NULL, NULL },
-#if 0
        { "bmp",  pixbuf_check_bmp, NULL,  NULL, NULL, NULL, NULL, NULL },
-#endif
        { NULL, NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
index f58a32e01b2bf5d8147a142767eb572d44be3b67..780a43324a02f72851ec509fe2c987decf52ba54 100644 (file)
@@ -1,8 +1,11 @@
-/*
- * io-bmp.c: GdkPixbuf I/O for BMP files.
+/* GdkPixbuf library - Windows Bitmap image loader
+ *
+ * Copyright (C) 1999 The Free Software Foundation
  *
- * Copyright (C) 1999 Mark Crichton
- * Author: Mark Crichton <crichton@gimp.org>
+ * Authors: Arjan van de Ven <arjan@fenrus.demon.nl>
+ *          Federico Mena-Quintero <federico@gimp.org>
+ *
+ * Based on io-ras.c
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
  * Library General Public License for more details.
  *
  * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Cambridge, MA 02139, USA.
- *
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
  */
 
+/*
+
+Known bugs:
+       * Compressed files don't work yet
+       * bi-tonal files aren't tested 
+
+*/
+
 #include <config.h>
 #include <stdio.h>
+#include <unistd.h>
+#include <sys/mman.h>
 #include <glib.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
 #include "gdk-pixbuf.h"
 #include "gdk-pixbuf-io.h"
-#include "io-bmp.h"
+\f
+
+
+struct headerpair {
+       guint width;
+       guint height;
+       guint depth;
+};
+
+struct BitmapFileHeader {
+       gushort bfType;
+       guint   bfSize;
+       guint   reserverd;
+       guint   bfOffbits;
+};
+
+struct BitmapInfoHeader {
+       guint   biSize;
+       guint   biWidth;
+       guint   biHeight;
+       gushort biPlanes;
+       gushort biBitCount;
+       guint   biCompression;
+       guint   biSizeImage;
+       guint   biXPelsPerMeter;
+       guint   biYPelsPerMeter;
+       guint   biClrUsed;
+       guint   biClrImportant;
+};
+
+/* 
+       This does a byte-order swap. Does glib have something like
+       be32_to_cpu() ??
+*/
+
+static unsigned int le32_to_cpu(guint i)
+{
+       unsigned int i2;
+       return i2;
+}
+
+/* 
+       Destroy notification function for the libart pixbuf 
+*/
+
+static void free_buffer(gpointer user_data, gpointer data)
+{
+       free(data);
+}
+
+
+
+
+/* Progressive loading */
+
+struct bmp_progressive_state {
+       ModulePreparedNotifyFunc prepared_func;
+       ModuleUpdatedNotifyFunc updated_func;
+       gpointer user_data;
+
+       gint HeaderSize;        /* The size of the header-part (incl colormap) */
+       guchar *HeaderBuf;      /* The buffer for the header (incl colormap) */
+       gint HeaderDone;        /* The nr of bytes actually in HeaderBuf */
+
+       gint LineWidth;         /* The width of a line in bytes */
+       guchar *LineBuf;        /* Buffer for 1 line */
+       gint LineDone;          /* # of bytes in LineBuf */
+       gint Lines;             /* # of finished lines */
+
+       gint Type;              /*  
+                                  24 = RGB
+                                  8 = 8 bit colormapped
+                                  1  = 1 bit bitonal 
+                                */
+
+
+       struct headerpair Header;       /* Decoded (BE->CPU) header */
+       
+       struct BitmapFileHeader  BFH;
+       struct BitmapInfoHeader  BIH;
+       
+
+
+       GdkPixbuf *pixbuf;      /* Our "target" */
+};
+
+gpointer
+image_begin_load(ModulePreparedNotifyFunc prepared_func,
+                ModuleUpdatedNotifyFunc updated_func, gpointer user_data);
+void image_stop_load(gpointer data);
+gboolean image_load_increment(gpointer data, guchar * buf, guint size);
+
 
-/* Loosely based off the BMP loader from The GIMP, hence it's complexity */
 
 /* Shared library entry point */
 GdkPixbuf *image_load(FILE * f)
 {
-       art_u8 *pixels;
-       ArtPixBuf *art_pixbuf;
+       guchar *membuf;
+       size_t length;
+       struct bmp_progressive_state *State;
+       int fd;
+       
+       GdkPixbuf *pb;
+       
+       State = image_begin_load(NULL, NULL, NULL);
+       
+       membuf = g_malloc(4096);
+       
+       g_assert(membuf != NULL);
+       
+       while (feof(f) == 0) {
+               length = fread(membuf, 1, 4096, f);
+               image_load_increment(State, membuf, length);
+       } 
+       g_free(membuf);
+       if (State->pixbuf != NULL)
+               gdk_pixbuf_ref(State->pixbuf);
+
+       pb = State->pixbuf;
+
+       image_stop_load(State);
+       return State->pixbuf;
+}
+
+static void DecodeHeader(unsigned char *BFH,unsigned char *BIH,
+                     struct bmp_progressive_state *State)
+{
+       State->Header.width = (BIH[7]<<24)+(BIH[6]<<16)+(BIH[5]<<8)+(BIH[4]);
+       State->Header.height = (BIH[11]<<24)+(BIH[10]<<16)+(BIH[9]<<8)+(BIH[8]);
+       State->Header.depth = (BIH[15]<<8)+(BIH[14]);;
+
+       State->Type = State->Header.depth;      /* This may be less trivial someday */
+       State->HeaderSize = 
+                       ((BFH[13]<<24)+(BFH[12]<<16)+(BFH[11]<<8)+(BFH[10]));                      
+
+       if (State->Type == 24)
+               State->LineWidth = State->Header.width * 3;
+       if (State->Type == 8)
+               State->LineWidth = State->Header.width * 1;
+       if (State->Type == 1) {
+               State->LineWidth = State->Header.width / 8;
+               if ((State->Header.width & 7) != 0)
+                       State->LineWidth++;
+       }
+       
+       /* Pad to a 32 bit boundary */
+       if ((State->LineWidth%4)>0)
+               State->LineWidth=(State->LineWidth%4)*4+4;
+       
+
+       if (State->LineBuf == NULL)
+               State->LineBuf = g_malloc(State->LineWidth);
+
+       g_assert(State->LineBuf != NULL);
+
+
+       if (State->pixbuf == NULL) {
+               State->pixbuf =
+                           gdk_pixbuf_new(ART_PIX_RGB, FALSE, 8,
+                                          (gint) State->Header.width,
+                                          (gint) State->Header.height);
+               if (State->prepared_func != NULL)
+                       /* Notify the client that we are ready to go */
+                       (*State->prepared_func) (State->pixbuf,
+                                                State->user_data);
+
+       }
+       
+}
+
+/* 
+ * func - called when we have pixmap created (but no image data)
+ * user_data - passed as arg 1 to func
+ * return context (opaque to user)
+ */
+
+gpointer
+image_begin_load(ModulePreparedNotifyFunc prepared_func,
+                ModuleUpdatedNotifyFunc updated_func, gpointer user_data)
+{
+       struct bmp_progressive_state *context;
+
+       context = g_new0(struct bmp_progressive_state, 1);
+       context->prepared_func = prepared_func;
+       context->updated_func = updated_func;
+       context->user_data = user_data;
+
+       context->HeaderSize = 54;
+       context->HeaderBuf = g_malloc(14 +
+                                     40 + 768);        
+                                                       /* 768 for the colormap */
+       context->HeaderDone = 0;
+
+       context->LineWidth = 0;
+       context->LineBuf = NULL;
+       context->LineDone = 0;
+       context->Lines = 0;
+
+       context->Type = 0;
+
+       memset(&context->Header, 0, sizeof(struct headerpair));
+
+
+       context->pixbuf = NULL;
+
+
+       return (gpointer) context;
+}
+
+/*
+ * context - returned from image_begin_load
+ *
+ * free context, unref gdk_pixbuf
+ */
+void image_stop_load(gpointer data)
+{
+       struct bmp_progressive_state *context =
+           (struct bmp_progressive_state *) data;
+
+
+       g_return_if_fail(context != NULL);
+
+       if (context->LineBuf != NULL)
+               g_free(context->LineBuf);
+       if (context->HeaderBuf != NULL)
+               g_free(context->HeaderBuf);
+
+       if (context->pixbuf)
+               gdk_pixbuf_unref(context->pixbuf);
+
+       g_free(context);
+}
+
+
+static void OneLine24(struct bmp_progressive_state *context)
+{
+       gint X;
+       guchar *Pixels;
+
+       X = 0;
+       Pixels = context->pixbuf->art_pixbuf->pixels +
+           context->pixbuf->art_pixbuf->rowstride * (context->Header.height-context->Lines);
+       while (X < context->Header.width) {
+               Pixels[X * 3 + 0] = context->LineBuf[X * 3 + 2];
+               Pixels[X * 3 + 1] = context->LineBuf[X * 3 + 1];
+               Pixels[X * 3 + 2] = context->LineBuf[X * 3 + 0];
+               X++;
+       }
+
+}
+
+static void OneLine8(struct bmp_progressive_state *context)
+{
+       gint X;
+       guchar *Pixels;
+
+       X = 0;
+       Pixels = context->pixbuf->art_pixbuf->pixels +
+           context->pixbuf->art_pixbuf->rowstride * (context->Header.height-context->Lines);
+       while (X < context->Header.width) {
+               /* The joys of having a BGR byteorder */
+               Pixels[X * 3 + 0] =
+                   context->HeaderBuf[4*context->LineBuf[X] + 56];
+               Pixels[X * 3 + 1] =
+                   context->HeaderBuf[4*context->LineBuf[X] + 55];
+               Pixels[X * 3 + 2] =
+                   context->HeaderBuf[4*context->LineBuf[X] + 54];
+               X++;
+       }
+}
+
+static void OneLine1(struct bmp_progressive_state *context)
+{
+       gint X;
+       guchar *Pixels;
+
+       X = 0;
+       Pixels = context->pixbuf->art_pixbuf->pixels +
+           context->pixbuf->art_pixbuf->rowstride * (context->Header.height-context->Lines);
+       while (X < context->Header.width) {
+               int Bit;
+               
+               Bit = (context->LineBuf[X/8])>>(7-(X&7));
+               Bit = Bit & 1;
+               /* The joys of having a BGR byteorder */
+               Pixels[X * 3 + 0] =
+                   context->HeaderBuf[Bit + 32];
+               Pixels[X * 3 + 1] =
+                   context->HeaderBuf[Bit + 2 + 32];
+               Pixels[X * 3 + 2] =
+                   context->HeaderBuf[Bit + 4 + 32];
+               X++;
+       }
+}
+
+
+static void OneLine(struct bmp_progressive_state *context)
+{
+       if (context->Type == 24)
+               OneLine24(context);
+       if (context->Type == 8)
+               OneLine8(context);
+       if (context->Type == 1)
+               OneLine1(context);
+
+       context->LineDone = 0;
+       if (context->Lines > context->Header.height)
+               return;
+       context->Lines++;
+
+       if (context->updated_func != NULL) {
+               (*context->updated_func) (context->pixbuf,
+                                         context->user_data,
+                                         0,
+                                         context->Lines,
+                                         context->Header.width,
+                                         context->Header.height);
+
+       }
+}
+
+/*
+ * context - from image_begin_load
+ * buf - new image data
+ * size - length of new image data
+ *
+ * append image data onto inrecrementally built output image
+ */
+gboolean image_load_increment(gpointer data, guchar * buf, guint size)
+{
+       struct bmp_progressive_state *context =
+           (struct bmp_progressive_state *) data;
+
+       gint BytesToCopy;
+
+       while (size > 0) {
+               if (context->HeaderDone < context->HeaderSize) {        /* We still 
+                                                                          have headerbytes to do */
+                       BytesToCopy =
+                           context->HeaderSize - context->HeaderDone;
+                       if (BytesToCopy > size)
+                               BytesToCopy = size;
+
+                       memcpy(context->HeaderBuf + context->HeaderDone,
+                              buf, BytesToCopy);
+
+                       size -= BytesToCopy;
+                       buf += BytesToCopy;
+                       context->HeaderDone += BytesToCopy;
+
+               } else {
+                       /* Pixeldata only */
+                       BytesToCopy =
+                           context->LineWidth - context->LineDone;
+                       if (BytesToCopy > size)
+                               BytesToCopy = size;
+
+                       if (BytesToCopy > 0) {
+                               memcpy(context->LineBuf +
+                                      context->LineDone, buf,
+                                      BytesToCopy);
+
+                               size -= BytesToCopy;
+                               buf += BytesToCopy;
+                               context->LineDone += BytesToCopy;
+                       }
+                       if ((context->LineDone >= context->LineWidth) &&
+                           (context->LineWidth > 0))
+                               OneLine(context);
+
+
+               }
+
+               if (context->HeaderDone >= 14+40)
+                       DecodeHeader(context->HeaderBuf,context->HeaderBuf+14,
+                                 context);
 
-       /* Ok, now stuff the GdkPixbuf with goodies */
 
-       if (is_trans)
-               art_pixbuf = art_pixbuf_new_rgba (pixels, w, h, (w * 4));
-       else
-               art_pixbuf = art_pixbuf_new_rgb  (pixels, w, h, (w * 3));
+       }
 
-       /* Ok, I'm anal...shoot me */
-       return gdk_pixbuf_new (art_pixbuf, NULL);
+       return TRUE;
 }